import asyncio
import time
from typing import *

import pandas as pd
from utils.exchange.binance import BinanceBase


class LoaderError(Exception):

    def __init__(self, info: str):
        self.info = info

    def __str__(self):
        return self.info

    def __repr__(self):
        return str(self)


class BaseLoader:
    date_column = 'open_time'

    def __init__(self, start: int = 0, end: int = 1, period: int = 3600,
                 with_decimal: bool = False, batch_size: int = 1):
        """

        :param start: 开始时间
        :param end: 结束时间
        :param period: K线基本周期
        :param with_decimal: 是否使用decimal类型
        :param batch_size: K线合并size
        """
        if start >= end:
            raise LoaderError(f'数据区间错误')
        self.start = start
        self.end = end
        self.cursor: int = start
        self.period = period
        self.batch_size = batch_size
        self.with_decimal = with_decimal

    def read(self) -> pd.DataFrame:
        pass

    def fetch(self, start: int = None, count: int = 1, end: int = None) -> pd.DataFrame:
        pass


class BinanceLoader(BaseLoader):
    def __init__(self, period: int = 3600, symbols: Sequence[str] = None, with_decimal: bool = False,
                 batch_size: int = 1):
        super().__init__(0, 1, period, with_decimal, batch_size=batch_size)
        self.symbols = symbols if symbols else None
        self.binance = BinanceBase(async_=True)

    def read(self) -> pd.DataFrame:
        raise LoaderError(f'binance类型不支持\'read\'')

    def fetch(self, start: int = None, count: int = None, end: int = None, type_: str = 'both',
              funding_rate: bool = True) -> pd.DataFrame:
        if start is None or end is None:
            raise LoaderError(f'binance类型获取K线须明确起止时间')
        if count is None:
            count = (end - start) // self.period

        df_spot = self.binance.kline(symbols=self.symbols, start_time=start, interval=self.period * self.batch_size,
                                     limit=count) if type_ != 'swap' else pd.DataFrame()
        df_swap = self.binance.kline(False, symbols=self.symbols, start_time=start,
                                     interval=self.period * self.batch_size,
                                     limit=count) if type_ != 'spot' else pd.DataFrame()
        df = pd.concat([df_spot, df_swap], ignore_index=True)
        if funding_rate and type_ != 'spot':
            df_funding = self.binance.funding_rate(start, symbols=self.symbols, end_time=end)
            df_funding.rename(columns={'fundingTime': 'open_time'}, inplace=True)
            df_funding.drop(columns=['markPrice'], inplace=True)

            return pd.merge(df, df_funding, how='left')
        return df

    def funding_rate(self, start: int, end: int) -> pd.DataFrame:
        df = self.binance.funding_rate(start, self.symbols, end_time=end)
        df['fundingTime'] -= df['fundingTime'] % 1000
        return df
